///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.controls
{
  import flash.events.Event;
  
  import ilog.gauges.gaugesClasses.LinearScale;
  import ilog.gauges.gaugesClasses.rectangular.IRectangularNumericScale;
  import ilog.gauges.gaugesClasses.rectangular.RectangularValueRenderer;
  import ilog.gauges.rectangular.RectangularGauge;
  import ilog.gauges.rectangular.RectangularLinearScale;
  import ilog.gauges.rectangular.renderers.RectangularLabelRenderer;
  import ilog.gauges.rectangular.renderers.RectangularScaleRenderer;
  import ilog.gauges.rectangular.renderers.RectangularTrackRenderer;
  import ilog.utils.CSSUtil;
  
  import mx.styles.CSSStyleDeclaration;
  import mx.core.UITextField;
  import mx.graphics.GradientEntry;
  
  [Exclude(name="scales", kind="property")]
  [Exclude(name="elements", kind="property")]        
    

  /**
   * Dispatched when the gauge value changes.
   *
   * @eventType flash.events.Event.CHANGE
   * 
   */
  [Event(name="change", type="flash.events.Event")]
  
  /**
   * Controls the visibility of the track.
   *  
   * @default true
   */
  [Style(name="showTrack", type="Boolean", inherit="no")]

  /**
   * Controls the visibility of the value indicator label.
   *  
   * @default true
   */
  [Style(name="showValueIndicator", type="Boolean", inherit="no")]

  /**
   * Controls the visibility of the scale labels.
   *  
   * @default true
   */
  [Style(name="showLabels", type="Boolean", inherit="no")]

  /**
   * Controls the visibility of the scale major ticks.
   *  
   * @default true
   */
  [Style(name="showMajorTicks", type="Boolean", inherit="no")]

  /**
   * Controls the visibility of the scale minor ticks.
   *  
   * @default true
   */
  [Style(name="showMinorTicks", type="Boolean", inherit="no")]

  /**
   * Specifies whether only minimum and maximum labels are shown. 
   *   
   * @default false
   */
  [Style(name="onlyMinMaxLabels", type="Boolean", inherit="no")]

  /**
   * The length, in milliseconds, 
   * of a transition when changing the <code>value</code>. 
   * 
   * @default 500
   */
  [Style(name="animationDuration", type="Number", format="Time", inherit="no")]
  
  /**
   * A style name to be used for the value indicator label. If <code>null</code>
   * the indicator label will use the style of the gauge itself.
   * 
   * @default null
   * @since ILOG Elixir 2.0
   * @ilog.reviewed SH April 24 08
   */
  [Style(name="valueIndicatorStyleName", type="String", inherit="no")]  
  
  /**
   * A style name to be used for the major tick renderers. Useful
   * for tick renderers that might use properties from this
   * style.
   * 
   * @default null
   * @since ILOG Elixir 2.0
   */
  [Style(name="majorTickStyleName", type="String", inherit="no")]

  /**
   * A style name to be used for the minor tick renderers. Useful
   * for tick renderers that might use properties from this
   * style.
   *
   * @default null 
   * @since ILOG Elixir 2.0   
   */
  [Style(name="minorTickStyleName", type="String", inherit="no")]      

  [Exclude(name="elements", kind="property")]
  
  /**
   * This is a base class for rectangular gauges that renders a single
   * value on a single numeric scale.
   * This class defines a rectangular gauge with the following elements:
   * <ul>
   *   <li>a scale, with configurable range and value</li>
   *   <li>a value renderer (typically a marker or bar)</li> 
   *   <li>a title label, at the top or bottom of the gauge</li>
   *   <li>a label, which shows the current value of the gauge</li> 
   * </ul>
   * <p>
   * When a given property is available on <code>NumericRectangularGauge</code>,
   * it overrides any corresponding property that may have been set on one of its predefined
   * elements. For example, the <code>minimum</code> property value of the 
   * <code>NumericRectangularGauge</code> overrides the <code>minimum</code>
   * property value of the <code>IRectangularNumericScale</code> referenced
   * by the <code>scale</code> property.
   * </p>
   * <p>
   * You do not use the <code>NumericRectangularGauge</code> class 
   * directly, even if it is possible. Instead, you use one of its subclasses, such as 
   * <code>SimpleRectangularGauge</code>, or you create your own subclass.
   * </p>
   *  
   * @mxml
   *  
   * <p>The <code>&lt;ilog:NumericRectangularGauge&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:NumericRectangularGauge
   *   <strong>Properties</strong>
   *   minimum="0"
   *   maximum="100"
   *   value="<i>Number, No default</i>"
   *   trackMinimum="50"
   *   trackMaximum="100"
   *   majorTickInterval="NaN"
   *   minorTickInterval="NaN"
   *   snapInterval="1"
   *   scale="<i>A default linear scale</i>"
   *   indicatorLabelFunction="<i>Function</i>"
   *   backgroundElements="<i>Array, No default</i>"
   *   foregroundElements="<i>Array, No default</i>"
   *   valueRenderer="<i>RectangularValueRenderer, No default</i>"
   *   scaleRenderer="<i>A default scale renderer</i>"
   *   track="<i>A default circular track</i>"
   *   editable="false|true"
   *   editMode="discrete|continuous"
   *   mouseMode="area|shape"    
   * 
   *   <strong>Styles</strong>
   *   showLabels="true|false"
   *   showMajorTicks="true|false"
   *   showMinorTicks="true|false" 
   *   showTrack="true|false"
   *   onlyMinMaxLabels="false|true"
   *   valueIndicatorStyleName="null"
   *   majorTickStyleName="null"
   *   minorTickStyleName="null"
   * /&gt;
   * </pre> 
   */    
  public class NumericRectangularGauge extends RectangularGauge
  {        
    private static var stylesInited:Boolean = initStyles(); 

    private static function initStyles():Boolean
    {
      var style:CSSStyleDeclaration =
        CSSUtil.createSelector("NumericRectangularGauge");   
            
      style.defaultFactory = function():void {
        this.showTrack = true;
        this.showTitle = true;
        this.showValueIndicator = true;
        this.onlyMinMaxLabels = false;
        this.showLabels = true;
        this.showMajorTicks = true;
        this.showMinorTicks = true;
        this.animationDuration = 500;
        this.majorTickStyleName = null;
        this.minorTickStyleName = null;
        this.valueIndicatorStyleName = null;        
      }
      
      return true;
    }    

    private var _valueLabel:RectangularLabelRenderer;
    private var _measuringLabel:UITextField;
    private var _elementsDirty:Boolean = false;
    
    private var _scaleParam:Object = {
      minimum : 0,
      maximum : 100,
      majorTickInterval : NaN,
      minorTickInterval : NaN,
      snapInterval: 1,
      dirty: false
    };
    
    private var _trackParam:Object = {
      minimum: 50,
      maximum: 100,
      dirty: false
    };

    /**
     * @private 
     */
    private var _valueRendererParam:Object = {
      editable: false,
      editMode: "discrete",
      mouseMode: "area", 
      dirty: false
    };
    
    /**
     * Constructor
     */
    public function NumericRectangularGauge() 
    {
      scale = new RectangularLinearScale();
      RectangularLinearScale(scale).snapInterval = 1;
      _valueLabel = new RectangularLabelRenderer();
    }    

    [Inspectable(category="Gauge")]
    
    /**
     * Specifies the increment to be used to snap values on this
     * scale during user interaction. For example, if <code>snapInterval</code> 
     * is 2, the <code>minimum</code> value is 0 and the <code>maximum</code> 
     * value is 10, the possible values returned by the scale for 
     * interactions are: 0, 2, 4, 6, 8, and 10.
     * A value of <code>NaN</code> means that no snapping takes place.
     * <strong>Warning:</strong> This property is only used if <code>scale</code> is a subclass of
     * <code>LinearScale</code>. 
     *
     * @default 1
     */
     public function get snapInterval():Number
     {
      return _scaleParam.snapInterval;
    }

    /**
     * @private 
     */
    public function set snapInterval(value:Number):void
    {
      _scaleParam.snapInterval = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }

    [Inspectable(category="Gauge")]
    
    /**
     * @copy ilog.gauges.gaugesClasses.rectangular.RectangularValueRenderer#editable
     * 
     * @default false
     */       
    public function get editable():Boolean
    {
      return _valueRendererParam.editable;
    }

    /**
     * @private 
     */
    public function set editable(value:Boolean):void
    {
      _valueRendererParam.editable = value;
      _valueRendererParam.dirty = true;
      invalidateProperties();
    }

    [Inspectable(category="Gauge", enumeration="continuous,discrete")]
    
    /**
     * @copy ilog.gauges.gaugesClasses.rectangular.RectangularValueRenderer#editMode
     * 
     * @default "discrete"
     */
    public function get editMode():String
    {
      return _valueRendererParam.editMode;
    }

    /**
     * @private 
     */
    public function set editMode(value:String):void
    {
      _valueRendererParam.editMode = value;
      _valueRendererParam.dirty = true;
      invalidateProperties();
    }

    [Inspectable(category="Gauge", enumeration="shape,area")]
    
    /**
     * @copy ilog.gauges.gaugesClasses.rectangular.RectangularValueRenderer#mouseMode
     * 
     * @default "area"
     */
    public function get mouseMode():String
    {
      return _valueRendererParam.mouseMode;
    }

    /**
     * @private 
     */
    public function set mouseMode(value:String):void
    {
      _valueRendererParam.mouseMode = value;
      _valueRendererParam.dirty = true;
      invalidateProperties();
    }
        
    [Bindable]
    [Inspectable(category="Gauge")]
    
    /**
     * The logical scale of the gauge.
     * 
     * @default ilog.gauges.rectangular.RectangularLinearScale 
     */   
    public function get scale():IRectangularNumericScale
    {
      return scales.length > 0 ? scales[0] : null;
    }
    
    /**
     * @private
     */
    public function set scale(value:IRectangularNumericScale):void 
    {
      scales = [value];
      _scaleParam.dirty = true;
      invalidateProperties();
    }

    private var _track:RectangularTrackRenderer;

    [Bindable]
    [Inspectable(category="Gauge")]    
    
    /**
     * A track. 
     * 
     * @default ilog.gauges.rectangular.renderers.RectangularTrackRenderer
     */      
    public function get track():RectangularTrackRenderer
    {
      return _track;
    }
    
    /**
     * @private
     */
    public function set track(value:RectangularTrackRenderer):void 
    {
      _track = value;
      _trackParam.dirty = true;
      invalidateElements();
    }

    private var _scaleRenderer:RectangularScaleRenderer;
  
    [Bindable]
    [Inspectable(category="Gauge")]    
    
    /**
     * The scale renderer of the gauge. 
     * 
     * @default ilog.gauges.rectangular.renderers.RectangularScaleRenderer
     */   
    public function get scaleRenderer():RectangularScaleRenderer
    {
      return _scaleRenderer;
    }
    
    /**
     * @private
     */
    public function set scaleRenderer(renderer:RectangularScaleRenderer):void
    {
      if (_scaleRenderer != null)
        _scaleRenderer.removeEventListener("tickAreaBoundsChanged", tickAreaBoundsChangedListener);
      _scaleRenderer = renderer;    
      if (_scaleRenderer != null) {
        _scaleRenderer.styleName = this;                
        _scaleRenderer.addEventListener("tickAreaBoundsChanged", tickAreaBoundsChangedListener);
      }      
      invalidateElements();
    }
    
    private function tickAreaBoundsChangedListener(event:Event):void
    {
      if (!_autoLayout) {
        return;
      }      
      layoutElements();
    }

    private var _valueRenderer:RectangularValueRenderer;
    
    [Bindable]
    [Inspectable(category="Gauge")]    
    
    /**
     * The value renderer of the gauge. Typically, a <code>MarkerRenderer</code>
     * or a <code>RectangularBarRenderer</code>.
     * 
     * @default null 
     */     
    public function get valueRenderer():RectangularValueRenderer
    {
      return _valueRenderer;
    }
    
    /**
     * @private
     */
    public function set valueRenderer(renderer:RectangularValueRenderer):void
    {
      if (_valueRenderer != null) {
        _valueRenderer.removeEventListener("change", changeListener);
        _valueRenderer.removeEventListener("valueCommit", changeListener);
      }
      _valueRenderer = renderer;
      _valueRendererParam.dirty = true;
      invalidateElements();
      if (_valueRenderer != null) {
        _valueRenderer.addEventListener("change", changeListener);
        _valueRenderer.addEventListener("valueCommit", changeListener);        
        // make sure the old value is put back on the new renderer, without animation
        // for now
        var duration:Number = getStyle("animationDuration");
        if (!isNaN(duration) && duration != 0) 
          _valueRenderer.setStyle("animationDuration", 0);
        _valueRenderer.value = _value;
        if (!isNaN(duration) && duration != 0)      
          _valueRenderer.setStyle("animationDuration", duration);        
      }
    }      
    
    private function changeListener(event:Event):void
    {
      _value = valueRenderer.value as Number;      
      if (indicatorLabelFunction != null)    
        _valueLabel.text = indicatorLabelFunction(valueRenderer.value);
      else {
        _valueLabel.text = isNaN(Number(valueRenderer.value)) ? "" : String(valueRenderer.value);
      }
      dispatchEvent(event);      
    }    
    
    /**
     * @private 
     */     
    private var _backgroundElements:Array = [];
    
    [Inspectable(category="Gauge")]    
    
    /**
     * An array of background <code>RectangularGaugeElement</code> objects that 
     * appear behind the scale, value renderer, track, and labels.
     * 
     * @default []
     */
    public function get backgroundElements():Array
    {
      return _backgroundElements;        
    }

    /**
     * @private 
     */     
    public function set backgroundElements(value:Array):void
    {
      _backgroundElements = value; 
      invalidateElements();
    }

    /**
     * @private 
     */   
    private var _foregroundElements:Array = [];

    [Inspectable(category="Gauge")]

    /**
     * An array of foreground <code>RectangularGaugeElement</code> objects that 
     * appear in front of the scale, value renderer, track, and labels.
     * 
     * @default []
     */
    public function get foregroundElements():Array
    {
      return _foregroundElements;
    }
    
    /**
     * @private 
     */    
    public function set foregroundElements(value:Array):void
    {
      _foregroundElements = value;
      invalidateElements();
    }
    
    private var _indicatorLabelFunction:Function;
    
    [Inspectable(category="Gauge")]    
    
    /**
     * The function called to format the text of the value indicator label.
     * An <code>indicatorLabelFunction</code> must have the following signature:
     * <pre>
     * function indicatorLabelFunction(value:Object):String { ... }
     * </pre>
     * If <code>indicatorLabelFunction</code> is <code>null</code>, the value is converted into a 
     * <code>String</code> object to be displayed. 
     * 
     * @default null
     * @ilog.reviewed SH 24 Oct 08
     */    
    public function get indicatorLabelFunction():Function
    {
      return _indicatorLabelFunction;
    }
    
    /**
     * @private
     */
    public function set indicatorLabelFunction(value:Function):void
    {
      _indicatorLabelFunction = value;
      adaptLabel();      
    }
    
    [Bindable]
    [Inspectable(category="Gauge", defaultValue="50")]
    
    /**
     * The minimum value of the track.
     * 
     * @default 50 
     */       
    public function get trackMinimum():Number
    {
      return (_track != null) ? _track.minimum as Number : _trackParam.minimum;
    }

    /**
     * @private 
     */   
    public function set trackMinimum(val:Number):void
    {
      _trackParam.minimum = val;
      _trackParam.dirty = true;
      invalidateProperties();
    }

    [Bindable]
    [Inspectable(category="Gauge", defaultValue="100")]
    
    /**
     * The maximum value of the track.
     *  
     * @default 100
     */       
    public function get trackMaximum():Number
    {
      return _trackParam.maximum;
    }

    /**
     * @private 
     */
    public function set trackMaximum(val:Number):void
    {
      _trackParam.maximum = val;
      _trackParam.dirty = true;
      invalidateProperties();
    }
    
    private var _value:Number = NaN;
    
    private var _valueDirty:Boolean = false;   
    
    [Bindable("change")]
    [Bindable("valueCommit")]
    [Inspectable(category="Gauge", defaultValue="null")]
    
    /**
     * The current value of the gauge, pointed to by the value renderer. 
     * 
     * @default NaN
     */       
    public function get value():Number
    {
      return _value;
    }

    /**
     * @private 
     */   
    public function set value(val:Number):void
    {
      _value = val;
      _valueDirty = true;
      invalidateProperties();
    }
    
    [Inspectable(category="Gauge", defaultValue="0")]
    
    /**
     * The minimum value of the scale.
     * 
     * @default 0 
     */       
    public function get minimum():Number
    {
      return _scaleParam.minimum;
    }

    /**
     * @private 
     */
    public function set minimum(value:Number):void
    {
      _scaleParam.minimum = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }
    
    [Inspectable(category="Gauge", defaultValue="100")]
        
    /**
     * The maximum value of the scale.
     * 
     * @default 100 
     */       
    public function get maximum():Number
    {
      return _scaleParam.maximum;
    }

    /**
     * @private 
     */
    public function set maximum(value:Number):void
    {
      _scaleParam.maximum = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }

    [Inspectable(category="Gauge")]
           
    /**
     * The interval between two major ticks. If <code>NaN</code>, the major
     * tick interval is automatically computed. The result depends on the type
     * of the scale.
     * 
     * @default NaN
     * 
     * @see ilog.gauges.gaugesClasses.LinearScale#majorTickInterval
     * @see ilog.gauges.gaugesClasses.LogScale#majorTickInterval
     */     
    public function get majorTickInterval():Number
    {
      return _scaleParam.majorTickInterval;
    }

    /**
     * @private 
     */
    public function set majorTickInterval(value:Number):void
    {
      _scaleParam.majorTickInterval = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }  

    [Inspectable(category="Gauge")]       
    
    /**
     * The interval between two minor ticks. If <code>NaN</code>, the minor
     * tick interval is automatically computed. This property is only supported if
     * the gauge scale is a <code>LinearScale</code> subclass.
     * 
     * @see #majorTickInterval
     * 
     * @default NaN
     * 
     * @see ilog.gauges.gaugesClasses.LinearScale#minorTickInterval
     */     
    public function get minorTickInterval():Number
    {      
      return _scaleParam.minorTickInterval;
    }

    /**
     * @private 
     */
    public function set minorTickInterval(value:Number):void
    {
      _scaleParam.minorTickInterval = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }  
    
    private var _autoLayout:Boolean = true;
    
    [Inspectable(category="Gauge")]
        
    /**
     * Specifies whether the gauge tries to force a specific layout on
     * its predefined elements. If <code>true</code>, the <code>layoutElements</code>
     * method is called when needed, otherwise it is not called.
     * 
     * @default true
     */
    public function get autoLayout():Boolean
    {
      return _autoLayout;
    }
    
    /**
     * @private
     */
    public function set autoLayout(value:Boolean):void
    {
      if (_autoLayout != value) {
        _autoLayout = value;
        if (_autoLayout) {
          layoutElements();
        }
      }
    }
    
    private var _autoAdaptValueLabel:Boolean = false;
    
    [Inspectable(category="Gauge")]    
    
    /**
     * If <code>true</code>, the gauge adapts the size of the value label to
     * the current gauge value. If <code>false</code>, it computes a size once, 
     * at start time, using the <code>maximum</code> value of the gauge.
     * 
     * @default false
     */
    public function get autoAdaptValueLabel():Boolean
    {
      return _autoAdaptValueLabel;
    }
    
    /**
     * @private
     */
    public function set autoAdaptValueLabel(value:Boolean):void
    {
      _autoAdaptValueLabel = value;
      adaptLabel();
    }
    
    private function adaptLabel():void
    {
      if (_autoAdaptValueLabel) {
        _valueLabel.minWidth = 0;
        _valueLabel.minHeight = 0;
      } else { 
        if (_measuringLabel == null)
          _measuringLabel = new UITextField();
        _measuringLabel.text = _indicatorLabelFunction != null ?
          _indicatorLabelFunction(maximum) : String(maximum);   
        _measuringLabel.setTextFormat(determineTextFormatFromStyles());
        _valueLabel.minWidth = _measuringLabel.measuredWidth;
        _valueLabel.minHeight = _measuringLabel.measuredHeight;     
      }
    }
    
    /**
     * This method is called to specify the layout of the predefined elements of 
     * the gauge. You can redefine it to position the elements differently.
     */
    protected function layoutElements():void
    {
      if (_valueRenderer != null) {
        if (_scaleRenderer != null) {
          _valueRenderer.x = _scaleRenderer.tickAreaBounds.x
          _valueRenderer.y = _scaleRenderer.tickAreaBounds.y;
          _valueRenderer.width = _scaleRenderer.tickAreaBounds.width;
          _valueRenderer.height = _scaleRenderer.tickAreaBounds.height;
        } else {
          _valueRenderer.percentX = 0;
          _valueRenderer.percentY = 0;
          _valueRenderer.percentWidth = 100;
          _valueRenderer.percentHeight = 100;          
        }
      }
      if (getStyle("showTrack") == true && _track != null) {
        if (_scaleRenderer != null) {            
          _track.x = _scaleRenderer.tickAreaBounds.x;
          _track.y = _scaleRenderer.tickAreaBounds.y;          
          _track.width = _scaleRenderer.tickAreaBounds.width;
          _track.height = _scaleRenderer.tickAreaBounds.height;            
        } else {
          _track.percentX = 0;
          _track.percentY = 0;
          _track.percentWidth = 100;
          _track.percentHeight = 100;
        }
      }
    }
    
    /**
     * Concatenates the various elements of the gauge and sets the 
     * <code>elements</code> property to the result. You can redefine this method
     * to customize the concatenation.
     * 
     * @see ilog.gauges.gaugesClasses.GaugeBase#elements
     */      
    protected function validateElements():void
    {
      var showTrack:Boolean = getStyle("showTrack");
      var showValueIndicator:Boolean = getStyle("showValueIndicator");
 
      var elts:Array = [];
      
      if (_backgroundElements && backgroundElements.length > 0)
        elts = elts.concat(_backgroundElements);    

      if (showTrack == true && _track != null) 
        elts = elts.concat(_track);
 
      if (_scaleRenderer != null)
        elts = elts.concat(_scaleRenderer);      
 
      if (showValueIndicator == true) {
        if (direction == "horizontal") {
          _valueLabel.area = "right";
        } else {
          _valueLabel.area = "top";            
        }
        elts = elts.concat(_valueLabel);
      }
      
      if (_valueRenderer != null)
        elts = elts.concat(_valueRenderer);
        
      if (_foregroundElements && foregroundElements.length > 0)
        elts = elts.concat(_foregroundElements);        
       
      elements = elts;
    }

    /**
     * Invalidates the <code>elements</code> array so that <code>validateElements</code>
     * is called.
     * 
     * @see ilog.gauges.gaugesClasses.GaugeBase#elements
     * @see #validateElements()
     */        
    protected function invalidateElements():void
    {
      _elementsDirty = true;
      invalidateProperties();
    }    
    
    /**
     * @private 
     */ 
    override public function styleChanged(styleProp:String):void
    {
      super.styleChanged(styleProp);
      // if the style props in one of the showXXX properties
      // let's rebuild the elements array accordingly
      if (styleProp != null && styleProp.indexOf("show") == 0)
        invalidateElements();
        
      if (styleProp == null || styleProp == "valueIndicatorStyleName") {
        _valueLabel.styleName = getStyle("valueIndicatorStyleName");
        adaptLabel();          
      }     

      if ((styleProp == null || styleProp.indexOf("font") == 0) && 
         getStyle("valueIndicatorStyleName") == null) 
        adaptLabel();

      if (styleProp == "animationDuration") {
        _valueRendererParam.dirty = true;
        invalidateProperties();
      }          
    }    
    
    /**
     * @private
     */
    protected override function commitProperties():void
    {
      // let's build what we need first?
      if (_elementsDirty) {
        _elementsDirty = false;        
        validateElements();
      }      
      if (_trackParam.dirty && _track != null && getStyle("showTrack") == true) {
        _track.minimum = _trackParam.minimum;
        _track.maximum = _trackParam.maximum;
        _trackParam.dirty = false; 
      }
      if (_scaleParam.dirty && scale != null) {
        scale.minimum = _scaleParam.minimum;
        scale.maximum = _scaleParam.maximum;        
        scale.majorTickInterval = _scaleParam.majorTickInterval;
        if (scale is LinearScale) {
          LinearScale(scale).minorTickInterval = _scaleParam.minorTickInterval;
          LinearScale(scale).snapInterval = _scaleParam.snapInterval;          
        }
        _scaleParam.dirty = false;       
      }
      if (_valueDirty && _valueRenderer != null) {
        valueRenderer.value = _value;
        _valueDirty = false; 
      }
      if (valueRenderer && _valueRendererParam.dirty) {
        valueRenderer.editable = _valueRendererParam.editable;
        valueRenderer.editMode = _valueRendererParam.editMode;
        valueRenderer.mouseMode = _valueRendererParam.mouseMode;
        valueRenderer.setStyle("animationDuration", getStyle("animationDuration"));
        _valueRendererParam.dirty = false; 
      }
      super.commitProperties();
    }
    
    /**
     * @private
     */
    protected override function createChildren():void
    {
      super.createChildren();
      if (scaleRenderer == null) {
        scaleRenderer = new RectangularScaleRenderer();      
      }
      if (track == null) {
        track = new RectangularTrackRenderer();       
        track.gradientEntries = [
          new GradientEntry(0x00DD00, 0),
          new GradientEntry(0xDDDD00, 0.5),
          new GradientEntry(0xDD0000, 1.0) 
        ];
      }
    }
  }
}
